
#include "gtmonitor.h"
#include "common.h"
#include "storage.h"
#include "process.h"
#include "cpu.h"
#include "memory.h"
#include "fs.h"
#include "power.h"
#include "network.h"

int loggable;
static int initiated = 0;
static pthread_t thread;
static int phone_rooted;
static int on_hold;
static int freeze = 0;
static int do_exit = 0;

/*
#define MAXSIZE 500

typedef struct DSK_array {
    struct DSK_array *next;
    char util[MAXSIZE];
} storage_array;

static char CPU_record_load[MAXSIZE], CPU_working_load[MAXSIZE];
static char MEM_record_load[MAXSIZE], MEM_working_load[MAXSIZE];
static int CPU_index;
static int MEM_index;
static int DSK_index;
storage_array DSK_record_load, DSK_working_load;
*/
void check_rooted()
{
    struct stat s;
    char *path = getenv("PATH");
    char *checkpath = (char *) malloc(strlen(path)+8);
    char checkfile[256];
    int check, last;

    if(!path || checkpath == 0) {
        if(lstat(SU_PATH, &s) < 0)
            phone_rooted = 0;
        else
            phone_rooted = 1;
    } else {
        phone_rooted = 0;
        last = 0;

        strncpy(checkpath, path, strlen(path));
        for (check = 0; check < strlen(path); check++) {
            if(checkpath[check] == ':') {
                checkpath[check] = '\x0';
                memset(checkfile, 0, 256);
                snprintf(checkfile, 256, "%s" , &checkpath[last], "/su");
                if(lstat(checkfile, &s) == 0) {
                    phone_rooted = 1;
                    break;
                }
            }
        }
    }

    __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "phone rooted %d\n", phone_rooted);
    if(checkpath != 0)
        free(checkpath);

    return;
}

void cleanup()
{
    cpu_cleanup();
    storage_cleanup();
    ps_cleanup();
    mem_cleanup();
    filesystem_cleanup();
    net_cleanup();
    /*
    storage_array *pt = DSK_record_load.next;
    while (pt != NULL) {
        storage_array *tmp = pt->next;
        free(pt);
        pt = tmp;
    }
    pt = DSK_working_load.next;
    while (pt != NULL) {
        storage_array *tmp = pt->next;
        free(pt);
        pt = tmp;
    }
    */
}

void *background_task(void *a)
{
    while (!do_exit) {
        sleep(1);
        if (on_hold) continue;
        // File system
        fs_dump();

        // Memory
        mem_dump();

        // CPU
        cpu_dump();
        refresh_cpu_usage();

        // GTmonitor
        ps_self_dump();
        ps_self_refresh();

        // Storage
        if (storage_dump()) {// refresh only if a storage device is found
            refresh_storage_util();
        }

        // Network
        net_dump();
        refresh_net();

        power_dump();
    }
    cleanup();
}
/*
void init_array()
{
    int i, size = get_storage_total();

    memset(CPU_record_load, 0, sizeof(CPU_record_load));
    memset(CPU_working_load, 0, sizeof(CPU_working_load));

    memset(MEM_record_load, 0, sizeof(MEM_record_load));
    memset(MEM_working_load, 0, sizeof(MEM_working_load));

    memset(&DSK_record_load, 0, sizeof(storage_array));
    storage_array *pt = DSK_record_load.next;
    for (i=1; i<size; ++i) {
        pt = (storage_array*)malloc(sizeof(storage_array));
        memset(pt, 0, sizeof(storage_array));
        pt = pt->next;
    }
    memset(&DSK_working_load, 0, sizeof(storage_array));
    pt = DSK_working_load.next;
    for (i=1; i<size; ++i) {
        pt = (storage_array*)malloc(sizeof(storage_array));
        memset(pt, 0, sizeof(storage_array));
        pt = pt->next;
    }

    CPU_index = MEM_index = DSK_index = 0;
}
*/

void create_dir()
{
    struct stat sb;
    if (stat(LOGGER_PATH, &sb) == 0 && S_ISDIR(sb.st_mode)) {
        loggable = 1;
    } else if (mkdir(LOGGER_PATH, S_IRWXU) == 0) {
        loggable = 1;
    } else loggable = 0;
    __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "loggable %d\n", loggable);
}

void init_library()
{
    if (initiated) return;
    do_exit = 0;
    freeze = 0;

    create_dir();

    check_rooted();

    // Initialize HZ
    get_HZ();

    // Initialize CPU
    init_cpu();

    // Initialize storage
    init_storage();

    // Initialize processes
    init_process();

    // Initialize memory
    init_memory();

    // Initialize filesystem
    init_filesystem();

    // Initialize power
    init_power();
    // Initialize recording arrays
    //init_array();

    // Initialize network
    init_network();

    initiated = 1;
    pthread_create(&thread, NULL, background_task, NULL);
    //__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "jni init done");
}



////////////////////////////////////////////////////////////////////////////////

// System
JNIEXPORT void JNICALL SYS_hold(JNIEnv *env, jobject thiz)
{
    on_hold = 1;
    return;
}

JNIEXPORT void JNICALL SYS_resume(JNIEnv *env, jobject thiz)
{
    on_hold = 0;
    return;
}

JNIEXPORT void JNICALL SYS_exit(JNIEnv *env, jobject thiz)
{
    do_exit = 1;
    return;
}

JNIEXPORT void JNICALL SYS_set_freeze(JNIEnv *env, jobject thiz, jint f)
{
    freeze = f;
    return;
}

JNIEXPORT void JNICALL SYS_set_cpu_logging(JNIEnv *env, jobject thiz, jboolean yes)
{
    is_cpu_recording = (int)yes;
    set_cpu_logging();
    return;
}

JNIEXPORT void JNICALL SYS_set_mem_logging(JNIEnv *env, jobject thiz, jboolean yes)
{
    is_mem_recording = (int)yes;
    set_mem_logging();
    return;
}

JNIEXPORT void JNICALL SYS_set_dsk_logging(JNIEnv *env, jobject thiz, jboolean yes)
{
    is_dsk_recording = (int)yes;
    set_dsk_logging();
    return;
}

JNIEXPORT void JNICALL SYS_set_net_logging(JNIEnv *env, jobject thiz, jboolean yes)
{
    is_net_recording = (int)yes;
    set_net_logging();
    return;
}

// Common
JNIEXPORT jint JNICALL COMMON_hz(JNIEnv *env, jobject thiz)
{
    jint ret = get_HZ();
    return ret;
}

// Storage
JNIEXPORT jint JNICALL STORAGE_total(JNIEnv *env, jobject thiz)
{
    jint ret = get_storage_total();
    return ret;
}

JNIEXPORT jfloat JNICALL STORAGE_totalUtil(JNIEnv *env, jobject thiz)
{
    jfloat ret = get_storage_total_util();
    return ret;
}

JNIEXPORT jfloat JNICALL STORAGE_util(JNIEnv *env, jobject thiz, jint position)
{
    jfloat ret = get_storage_util(position);
    return ret;
}

JNIEXPORT jstring JNICALL STORAGE_name(JNIEnv *env, jobject thiz, jint position)
{
    char buf[BUFFERSIZE];
    get_storage_name(buf, position);
    //__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "storage name %s", buf);
    return (*env)->NewStringUTF(env, buf);
}

JNIEXPORT jlong JNICALL STORAGE_interval(JNIEnv *env, jobject thiz)
{
    jlong ret = get_interval_value();
    return ret;
}

JNIEXPORT jlong JNICALL STORAGE_sdctot(JNIEnv *env, jobject thiz, jint position)
{
    jlong ret = get_sdctot(position);
    return ret;
}

JNIEXPORT jlong JNICALL STORAGE_sdptot(JNIEnv *env, jobject thiz, jint position)
{
    jlong ret = get_sdptot(position);
    return ret;
}

// CPU
JNIEXPORT jint JNICALL CPU_usage(JNIEnv *env, jobject thiz)
{
    jint ret = get_cpu_usage();
    return ret;
}

JNIEXPORT jlong JNICALL CPU_deltatime(JNIEnv *env, jobject thiz)
{
    jlong ret = get_cpu_deltatime();
    return ret;
}

JNIEXPORT jlong JNICALL CPU_idle(JNIEnv *env, jobject thiz)
{
    jlong ret = get_cpu_idle();
    return ret;
}

// Memory
JNIEXPORT jlong JNICALL MEM_total(JNIEnv *env, jobject thiz)
{
    jlong ret = get_mem_total();
    return ret;
}

JNIEXPORT jlong JNICALL MEM_free(JNIEnv *env, jobject thiz)
{
    jlong ret = get_mem_free();
    return ret;
}

JNIEXPORT jlong JNICALL MEM_buffers(JNIEnv *env, jobject thiz)
{
    jlong ret = get_mem_buffers();
    return ret;
}

JNIEXPORT jlong JNICALL MEM_cached(JNIEnv *env, jobject thiz)
{
    jlong ret = get_mem_cached();
    return ret;
}

// Network
JNIEXPORT jlong JNICALL NET_total_rx(JNIEnv *env, jobject thiz)
{
    jlong ret = get_net_total_rx();
    return ret;
}

JNIEXPORT jlong JNICALL NET_total_tx(JNIEnv *env, jobject thiz)
{
    jlong ret = get_net_total_tx();
    return ret;
}

JNIEXPORT jfloat JNICALL NET_total_rx_speed(JNIEnv *env, jobject thiz)
{
    jfloat ret = get_net_total_rx_speed();
    return ret;
}

JNIEXPORT jfloat JNICALL NET_total_tx_speed(JNIEnv *env, jobject thiz)
{
    jfloat ret = get_net_total_tx_speed();
    return ret;
}

JNIEXPORT jlong JNICALL NET_rx(JNIEnv *env, jobject thiz, jint position)
{
    jlong ret = get_net_rx_by_index(position);
    return ret;
}

JNIEXPORT jlong JNICALL NET_tx(JNIEnv *env, jobject thiz, jint position)
{
    jlong ret = get_net_tx_by_index(position);
    return ret;
}

JNIEXPORT jint JNICALL NET_rx_speed(JNIEnv *env, jobject thiz, jint position)
{
    jlong ret = get_net_rxspeed_by_index(position);
    return ret;
}

JNIEXPORT jint JNICALL NET_tx_speed(JNIEnv *env, jobject thiz, jint position)
{
    jlong ret = get_net_txspeed_by_index(position);
    return ret;
}

JNIEXPORT jstring NET_name(JNIEnv* env, jobject thiz, jint position)
{
    char buf[BUFFERSIZE];
    get_net_name_by_index(position, buf);
    //__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "jstring pos: %d buffer: %s", position, buf);
    return (*env)->NewStringUTF(env, buf);
}

JNIEXPORT jint JNICALL NET_nbr(JNIEnv *env, jobject thiz)
{
    jint ret = get_net_nbr();
    return ret;
}

// Process
JNIEXPORT jint JNICALL PS_self_load(JNIEnv *env, jobject thiz)
{
    jint ret = get_ps_self_load();
    return ret;
}

JNIEXPORT jint JNICALL PS_total(JNIEnv *env, jobject thiz)
{
    jint ret = get_ps_counts();
    return ret;
}

JNIEXPORT jint JNICALL PS_threads(JNIEnv *env, jobject thiz, jint position)
{
    jint ret = get_threads_by_index(position);
    return ret;
}

JNIEXPORT jint JNICALL PS_pid(JNIEnv *env, jobject thiz, jint position)
{
    jint ret = get_pid_by_index(position);
    return ret;
}

JNIEXPORT jint JNICALL PS_uid(JNIEnv *env, jobject thiz, jint position)
{
    jint ret = get_uid_by_index(position);
    return ret;
}

JNIEXPORT jlong JNICALL PS_stime(JNIEnv *env, jobject thiz, jint position)
{
    jlong ret = get_stime_by_index(position);
    return ret;
}

JNIEXPORT jlong JNICALL PS_utime(JNIEnv *env, jobject thiz, jint position)
{
    jlong ret = get_utime_by_index(position);
    return ret;
}

JNIEXPORT jint JNICALL PS_load(JNIEnv *env, jobject thiz, jint position)
{
    jint ret = get_load_by_index(position);
    return ret;
}

JNIEXPORT jint JNICALL PS_rss(JNIEnv *env, jobject thiz, jint position)
{
    jint ret = get_rss_by_index(position);
    return ret;
}

JNIEXPORT jstring PS_name(JNIEnv* env, jobject thiz, jint position)
{
    char buf[BUFFERSIZE];
    get_name_by_index(position, buf);
    //__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "jstring pos: %d buffer: %s", position, buf);
    return (*env)->NewStringUTF(env, buf);
}

JNIEXPORT jstring PS_name_by_uid(JNIEnv* env, jobject thiz, jint uid)
{
    char buf[BUFFERSIZE];
    get_name_by_uid(uid, buf);
    return (*env)->NewStringUTF(env, buf);
}

JNIEXPORT void JNICALL PS_set_filter(JNIEnv *env, jobject thiz, jint noSystem)
{
    ps_set_no_system_process(noSystem);
    return;
}


JNIEXPORT jlong JNICALL FS_systemTotal(JNIEnv *env, jobject thiz)
{
    jlong ret = get_fs_system_total();
    return ret;
}

JNIEXPORT jlong JNICALL FS_systemAva(JNIEnv *env, jobject thiz)
{
    jlong ret = get_fs_system_ava();
    return ret;
}

JNIEXPORT jlong JNICALL FS_systemUsed(JNIEnv *env, jobject thiz)
{
    jlong ret = get_fs_system_used();
    return ret;
}

JNIEXPORT jlong JNICALL FS_sdcardTotal(JNIEnv *env, jobject thiz)
{
    jlong ret = get_fs_sdcard_total();
    return ret;
}

JNIEXPORT jlong JNICALL FS_sdcardAva(JNIEnv *env, jobject thiz)
{
    jlong ret = get_fs_sdcard_ava();
    return ret;
}

JNIEXPORT jlong JNICALL FS_sdcardUsed(JNIEnv *env, jobject thiz)
{
    jlong ret = get_fs_sdcard_used();
    return ret;
}

JNIEXPORT jlong JNICALL FS_cacheTotal(JNIEnv *env, jobject thiz)
{
    jlong ret = get_fs_cache_total();
    return ret;
}

JNIEXPORT jlong JNICALL FS_cacheAva(JNIEnv *env, jobject thiz)
{
    jlong ret = get_fs_cache_ava();
    return ret;
}

JNIEXPORT jlong JNICALL FS_cacheUsed(JNIEnv *env, jobject thiz)
{
    jlong ret = get_fs_cache_used();
    return ret;
}

JNIEXPORT jlong JNICALL FS_dataTotal(JNIEnv *env, jobject thiz)
{
    jlong ret = get_fs_data_total();
    return ret;
}

JNIEXPORT jlong JNICALL FS_dataAva(JNIEnv *env, jobject thiz)
{
    jlong ret = get_fs_data_ava();
    return ret;
}

JNIEXPORT jlong JNICALL FS_dataUsed(JNIEnv *env, jobject thiz)
{
    jlong ret = get_fs_data_used();
    return ret;
}

JNIEXPORT jint JNICALL POWER_getCapacity(JNIEnv *env, jobject thiz)
{
    jint ret = get_power_capacity();
    return ret;
}

JNIEXPORT jint JNICALL POWER_getVoltage(JNIEnv *env, jobject thiz)
{
    jint ret = get_power_voltage();
    return ret;
}

JNIEXPORT jint JNICALL POWER_getTemperature(JNIEnv *env, jobject thiz)
{
    jint ret = get_power_temperature();
    return ret;
}

JNIEXPORT jint JNICALL POWER_getACOnline(JNIEnv *env, jobject thiz)
{
    jint ret = get_power_aconline();
    return ret;
}

JNIEXPORT jint JNICALL POWER_getUSBOnline(JNIEnv *env, jobject thiz)
{
    jint ret = get_power_usbonline();
    __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "usbonline %d\n", ret);
    return ret;
}

JNIEXPORT jstring JNICALL POWER_getHealth(JNIEnv *env, jobject thiz)
{
    char buf[BUFFERSIZE];
    get_power_health(buf);
    return (*env)->NewStringUTF(env, buf);
}

JNIEXPORT jstring JNICALL POWER_getStatus(JNIEnv *env, jobject thiz)
{
    char buf[BUFFERSIZE];
    get_power_status(buf);
    return (*env)->NewStringUTF(env, buf);
}

JNIEXPORT jstring JNICALL POWER_getTechnology(JNIEnv *env, jobject thiz)
{
    char buf[BUFFERSIZE];
    get_power_technology(buf);
    return (*env)->NewStringUTF(env, buf);
}

static const char *classPathName = "com/gtmonitor/JNIInterface";

static JNINativeMethod gMethods[] = {

    /* name,               signature,          funcPtr*/
    // System
    {"hold",               "()V",              SYS_hold},
    {"resume",             "()V",              SYS_resume},
    {"exit",               "()V",              SYS_exit},
    {"setFreeze",          "(I)V",             SYS_set_freeze},
    {"setCPULogging",      "(Z)V",             SYS_set_cpu_logging},
    {"setMemLogging",      "(Z)V",             SYS_set_mem_logging},
    {"setDskLogging",      "(Z)V",             SYS_set_dsk_logging},
    {"setNetLogging",      "(Z)V",             SYS_set_net_logging},

    // Common
    {"getHZ",              "()I",              COMMON_hz},

    // Storage
    {"getStorageTotal",    "()I",              STORAGE_total},
    {"getStorageTotalUtil","()F",              STORAGE_totalUtil},
    {"getStorageUtil",     "(I)F",             STORAGE_util},
    {"getInterval",        "()J",              STORAGE_interval},
    {"getSdptot",          "(I)J",             STORAGE_sdptot},
    {"getSdctot",          "(I)J",             STORAGE_sdctot},
    {"getStorageName",     "(I)Ljava/lang/String;", STORAGE_name},

    // CPU
    {"getCPUUsage",        "()I",              CPU_usage},
    {"getCPUDeltaTime",    "()J",              CPU_deltatime},
    {"getCPUIdle",         "()J",              CPU_idle},

    // Memory
    {"getMemTotal",        "()J",              MEM_total},
    {"getMemFree",         "()J",              MEM_free},
    {"getMemBuffers",      "()J",              MEM_buffers},
    {"getMemCached",       "()J",              MEM_cached},

    // Network
    {"getNetTotalRx",      "()J",              NET_total_rx},
    {"getNetTotalTx",      "()J",              NET_total_tx},
    {"getNetTotalRxSpeed", "()F",              NET_total_rx_speed},
    {"getNetTotalTxSpeed", "()F",              NET_total_tx_speed},
    {"getNetRx",           "(I)J",             NET_rx},
    {"getNetTx",           "(I)J",             NET_tx},
    {"getNetRxSpeed",      "(I)I",             NET_rx_speed},
    {"getNetTxSpeed",      "(I)I",             NET_tx_speed},
    {"getNetName",         "(I)Ljava/lang/String;", NET_name},
    {"getNetNumber",       "()I",              NET_nbr},

    // Process
    {"getSelfLoad",        "()I",              PS_self_load},
    {"getProcessTotal",    "()I",              PS_total},
    {"getProcessThreads",  "(I)I",             PS_threads},
    {"getProcessPID",      "(I)I",             PS_pid},
    {"getProcessUID",      "(I)I",             PS_uid},
    {"getProcessStime",    "(I)J",             PS_stime},
    {"getProcessUtime",    "(I)J",             PS_utime},
    {"getProcessLoad",     "(I)I",             PS_load},
    {"getProcessRSS",      "(I)I",             PS_rss},
    {"getProcessName",     "(I)Ljava/lang/String;", PS_name},
    {"getProcessNameByUid","(I)Ljava/lang/String;", PS_name_by_uid},
    {"setProcessFilter",   "(I)V",             PS_set_filter},

    // File system
    {"getSystemTotal",     "()J",              FS_systemTotal},
    {"getSystemAvailable", "()J",              FS_systemAva},
    {"getSystemUsed",      "()J",              FS_systemUsed},
    {"getSDCardTotal",     "()J",              FS_sdcardTotal},
    {"getSDCardAvailable", "()J",              FS_sdcardAva},
    {"getSDCardUsed",      "()J",              FS_sdcardUsed},
    {"getCacheTotal",      "()J",              FS_cacheTotal},
    {"getCacheAvailable",  "()J",              FS_cacheAva},
    {"getCacheUsed",       "()J",              FS_cacheUsed},
    {"getDataTotal",       "()J",              FS_dataTotal},
    {"getDataAvailable",   "()J",              FS_dataAva},
    {"getDataUsed",        "()J",              FS_dataUsed},

    // Power
    { "GetPowerCapacity",  "()I",              POWER_getCapacity},
    { "GetPowerVoltage",   "()I",              POWER_getVoltage},
    { "GetPowerTemperature", "()I",            POWER_getTemperature},
    { "GetACOnline",       "()I",              POWER_getACOnline},
    { "GetUSBOnline",      "()I",              POWER_getUSBOnline},
    { "GetPowerHealth",    "()Ljava/lang/String;", POWER_getHealth},
    { "GetPowerStatus",    "()Ljava/lang/String;", POWER_getStatus},
    { "GetPowerTechnology", "()Ljava/lang/String;", POWER_getTechnology},
};

// Crash Handler
static JNIEnv *env;
static jobject obj;
static jmethodID nativeCrashed;
static struct sigaction old_sa[NSIG];

void android_sigaction(int signal, siginfo_t *info, void *reserved)
{
    (*env)->CallVoidMethod(env, obj, nativeCrashed);
    old_sa[signal].sa_handler(signal);
}

jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
    jclass cls, sgcls;
    
    if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_4)) {
        __android_log_print(ANDROID_LOG_INFO, LOG_TAG, 
                            "RETURN JNI_ERR\n"); 
        return JNI_ERR;
    }
    cls = (*env)->FindClass(env, classPathName);
    sgcls = (*env)->FindClass(env, "com/gtmonitor/GTmonitorActivity");
    (*env)->RegisterNatives(env, cls, gMethods, 
                            sizeof(gMethods)/sizeof(gMethods[0]));
    nativeCrashed  = (*env)->GetMethodID(env, sgcls,  "nativeCrashed", "()V");
    
    init_library();
    __android_log_print(ANDROID_LOG_INFO, LOG_TAG, 
                        "JNI_Onload finished\n"); 
    struct sigaction handler;
    memset(&handler, 0, sizeof(sigaction));
    handler.sa_sigaction = android_sigaction;
    handler.sa_flags = SA_RESETHAND;
    #define CATCHSIG(X) sigaction(X, &handler, &old_sa[X])
    CATCHSIG(SIGILL);
    CATCHSIG(SIGABRT);
    CATCHSIG(SIGBUS);
    CATCHSIG(SIGFPE);
    CATCHSIG(SIGSEGV);
    CATCHSIG(SIGSTKFLT);
    CATCHSIG(SIGPIPE);

    return JNI_VERSION_1_4;
}

void JNI_OnUnload(JavaVM *vm, void *reserved)
{
    JNIEnv *env;
    jclass cls;
    
    if ((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_4))
        return;
    
    cls = (*env)->FindClass(env, classPathName);
    (*env)->UnregisterNatives(env, cls);
    __android_log_print(ANDROID_LOG_INFO, LOG_TAG,
                            "JNI_OnUnload finished\n");
    return;
}

